bugfix(contain): Restore retail compatibility after crash fix in OpenContain::processDamageToContained#2427
Conversation
|
| Filename | Overview |
|---|---|
| Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h | Adds processDamageToContainedInternal declaration under #if RETAIL_COMPATIBLE_CRC; placed in public section instead of private |
| GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h | Mirrors Generals header change — same public placement issue for processDamageToContainedInternal |
| Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp | Replaces the fragile size-change break guard with a snapshot-copy approach; stack array used for containers <16, std::vector for larger; logic is correct |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp | Same snapshot approach as Generals; correctly uses m_isBurnedDeathToUnits for death type in Zero Hour; deathType hoisted out of per-object loop for the non-retail path |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[processDamageToContained] --> B{RETAIL_COMPATIBLE_CRC?}
B -- Yes --> C[Assert m_containListSize == m_containList.size]
C --> D{m_containListSize < 16?}
D -- Yes --> E[Stack copy: Object* containCopy-16]
D -- No --> F[Heap copy: std::vector containCopy]
E --> G[processDamageToContainedInternal\nstack array, m_containListSize]
F --> H[processDamageToContainedInternal\nvector data, vector size]
G --> I[For each object in snapshot:\nattemptDamage + optional kill\nOriginal list NOT modified here]
H --> I
B -- No --> J[Swap m_containList into local list\nm_containListSize = 0]
J --> K[For each object in local list:\nattemptDamage + optional kill\nErase dead objects from local list]
K --> L[Swap local list back to m_containList\nUpdate m_containListSize]
Prompt To Fix All With AI
This is a comment left during a code review.
Path: Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h
Line: 208-210
Comment:
**`processDamageToContainedInternal` should be `private`**
This method is only ever called by `processDamageToContained` within the same class and is an implementation detail not intended for external callers — its "Internal" suffix makes this clear. Declaring it `public` in both `Generals/` and `GeneralsMD/` headers unnecessarily widens the API surface.
```suggestion
private:
#if RETAIL_COMPATIBLE_CRC
void processDamageToContainedInternal(Object* const* objects, size_t size, Real percentDamage);
#endif
public:
```
The same applies to `GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h` line 222–224.
How can I resolve this? If you propose a fix, please make it concise.Reviews (7): Last reviewed commit: "Replicated in Generals (manually)." | Re-trigger Greptile
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
b533974 to
b342a9f
Compare
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
6c35643 to
4d5d684
Compare
|
I reset the branch and broke the new diff into manageable commits. |
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
|
Addressed feedback. In the unlikely scenario that the new implementation turns out not to be fully retail compatible, I expect that adding the following code here would fix it: if (size != m_containListSize)
{
if (m_containListSize == 0)
break;
if (object->isEffectivelyDead())
continue;
if (std::find(m_containList.begin(), m_containList.end(), object) == m_containList.end())
continue;
} |
|
I can replicate in Generals unless there are other changes desired. |
6c196ce to
9ad8a44
Compare
The way the crash fix was implemented in #1019 assumes the container size is either unchanged or 0. This is not the case in the attached replay in #2223.
Gameplay without this PR:
processDamageToContained_before2.mp4
Gameplay with this PR:
processDamageToContained_after2.mp4
Due to a different bug, a GLA Worker enters the unmanned Emperor Overlord but becomes part of the crew instead of the pilot. When the Emperor gets destroyed it first removes its Gattling turret and so the container size has changed. The Worker is still inside, though, so breaking out of the loop is not allowed.
TODO: